import {
  CallHandler,
  ExecutionContext,
  Inject,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { map, Observable, tap } from 'rxjs';
import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';
import {
  NO_NEED_PATH,
  ResponseCheckPath,
} from '../../constants/response-check-path';
import { RetUtils } from '../../utils/ret.utils';
import { CommonException } from '../exception/common.exception';
import StatusCode from '../../constants/status-code';

/**
 * 全局响应拦截器，统一返回体内容
 */
// 每次请求计数器
let requestSeq = 0;

@Injectable()
export class ResponseInterceptor implements NestInterceptor {
  constructor(
    @Inject(WINSTON_MODULE_NEST_PROVIDER)
    private readonly logger: Logger,
  ) {}

  intercept(
    context: ExecutionContext,
    next: CallHandler<any>,
  ): Observable<any> | Promise<Observable<any>> {
    return next.handle().pipe(
      map((data) => {
        // 开始时间
        const start = Date.now();
        // 当前环境
        const host = context.switchToHttp();
        // 请求对象
        const req = host.getRequest<Request>();
        // 响应对象
        const res = host.getResponse<Response>();
        // 当前计数
        const seq = requestSeq++;
        // 当前url
        const url = req.url;

        // 使用日志记录下调用情况
        this.logger.log('info', `[${seq}]==> ${req.method} ${url}`);

        // 检测下需要验证的api
        let needCheck = true;
        for (const pre of NO_NEED_PATH) {
          if (url.startsWith(pre)) {
            needCheck = false;
            break;
          }
        }
        // 需要验证的话
        if (needCheck) {
          if (data === undefined) {
            throw new CommonException(StatusCode.NOT_RETURN, '没有返回值');
          } else if (data) {
            return RetUtils.ret(StatusCode.OK, '请求成功', data, url);
          }
        } else {
          // 不需要验证 , 打印请求处理完的信息
          tap(() =>
            this.logger.log(
              'info',
              `请求${seq}：${req.method} ${url}，耗时：${Date.now() - start}ms`,
            ),
          );
          return data;
        }
      }),
    );
  }
}
